1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkNotNull;
21  
22  import com.google.common.annotations.GwtCompatible;
23  
24  import java.util.EnumMap;
25  import java.util.Map;
26  
27  /**
28   * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values
29   * are not permitted. An {@code EnumBiMap} and its inverse are both
30   * serializable.
31   * 
32   * <p>See the Guava User Guide article on <a href=
33   * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap">
34   * {@code BiMap}</a>.
35   *
36   * @author Mike Bostock
37   * @since 2.0 (imported from Google Collections Library)
38   */
39  @GwtCompatible(emulated = true)
40  public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>>
41      extends AbstractBiMap<K, V> {
42    private transient Class<K> keyType;
43    private transient Class<V> valueType;
44  
45    /**
46     * Returns a new, empty {@code EnumBiMap} using the specified key and value
47     * types.
48     *
49     * @param keyType the key type
50     * @param valueType the value type
51     */
52    public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
53        create(Class<K> keyType, Class<V> valueType) {
54      return new EnumBiMap<K, V>(keyType, valueType);
55    }
56  
57    /**
58     * Returns a new bimap with the same mappings as the specified map. If the
59     * specified map is an {@code EnumBiMap}, the new bimap has the same types as
60     * the provided map. Otherwise, the specified map must contain at least one
61     * mapping, in order to determine the key and value types.
62     *
63     * @param map the map whose mappings are to be placed in this map
64     * @throws IllegalArgumentException if map is not an {@code EnumBiMap}
65     *     instance and contains no mappings
66     */
67    public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
68        create(Map<K, V> map) {
69      EnumBiMap<K, V> bimap = create(inferKeyType(map), inferValueType(map));
70      bimap.putAll(map);
71      return bimap;
72    }
73  
74    private EnumBiMap(Class<K> keyType, Class<V> valueType) {
75      super(WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
76          WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
77      this.keyType = keyType;
78      this.valueType = valueType;
79    }
80  
81    static <K extends Enum<K>> Class<K> inferKeyType(Map<K, ?> map) {
82      if (map instanceof EnumBiMap) {
83        return ((EnumBiMap<K, ?>) map).keyType();
84      }
85      if (map instanceof EnumHashBiMap) {
86        return ((EnumHashBiMap<K, ?>) map).keyType();
87      }
88      checkArgument(!map.isEmpty());
89      return map.keySet().iterator().next().getDeclaringClass();
90    }
91  
92    private static <V extends Enum<V>> Class<V> inferValueType(Map<?, V> map) {
93      if (map instanceof EnumBiMap) {
94        return ((EnumBiMap<?, V>) map).valueType;
95      }
96      checkArgument(!map.isEmpty());
97      return map.values().iterator().next().getDeclaringClass();
98    }
99  
100   /** Returns the associated key type. */
101   public Class<K> keyType() {
102     return keyType;
103   }
104 
105   /** Returns the associated value type. */
106   public Class<V> valueType() {
107     return valueType;
108   }
109 
110   @Override
111   K checkKey(K key) {
112     return checkNotNull(key);
113   }
114 
115   @Override
116   V checkValue(V value) {
117     return checkNotNull(value);
118   }
119 }
120